/**
 * 1. 初始化地图
 *    1.1 随机生成，地雷
 *    1.2 找到每个点周边的地雷数
 */

const GAME_STATUS = {
  START: "start",
  END: "end",
  WIN: "win",
};

class Node {
  constructor() {
    this.mineNumberByAround = 0;
    this.opened = false;
    this.isFlag = false;
    this.isMine = false;
  }
}

let g = null; // 存储周围的地雷个数
let gameStatus = null; // 存储游戏当前的状态
let inputColEl = document.querySelector("#col");
let inputRowEl = document.querySelector("#row");
let inputMineNumEl = document.querySelector("#mineNumber");
/**
 * 设置每个 item 的长宽为 20 x 20
 */

// 初始化地图数组
// n: 宽，m：高
function initMapArray(n, m, mineCount) {
  // 初始化地图
  g = Array.from({ length: n }, () =>
    new Array(m).fill(0).map(() => new Node())
  );
  gameStatus = GAME_STATUS.START;

  const randomMine = (n, m) => {
    const x = Math.floor(Math.random() * (n - 1));
    const y = Math.floor(Math.random() * (m - 1));
    return [x, y];
  };
  // 生成自定地雷的函数
  while (mineCount > 0) {
    let [x, y] = randomMine(n, m);
    while (g[x][y].isMine) {
      [x, y] = randomMine(n, m);
    }
    g[x][y].isMine = true;
    mineCount--;
  }

  // 第二部计算每个不是地雷的item周围的地雷个数, 3 x 3 周围
  const d = [
    [0, 1],
    [1, 0],
    [0, -1],
    [-1, 0],
  ];
  const computerAroundMine = (x, y) => {
    let count = 0;

    for (let i = 0; i < 4; ++i) {
      let nx = x + d[i][0];
      let ny = y + d[i][1];
      // 判断nx，ny是否越界
      if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;

      if (g[nx][ny].isMine) count++;
    }

    return count;
  };

  for (let i = 0; i < n; ++i)
    for (let j = 0; j < m; ++j)
      if (!g[i][j].isMine) {
        g[i][j].mineNumberByAround = computerAroundMine(i, j);
      }
}
// 初始化地图UI
function initUI() {
  // 获取容器结点
  const gameEl = document.querySelector(".game");
  const vContainer = document.createDocumentFragment();

  // 获取长宽
  const n = g.length;
  const m = g[0].length;

  // 求出容器的宽高
  const gameElWidth = n * 20;
  const gameElHeight = m * 20;

  // 创建子结点，并初始化
  for (let i = 0; i < n; ++i)
    for (let j = 0; j < m; ++j) {
      const item = document.createElement("div");
      item.setAttribute("x", i);
      item.setAttribute("y", j);
      item.classList.add("game-item", `game-item-${i}-${j}`);
      if (g[i][j].isMine) item.classList.add("mine");
      item.style.width = "20px";
      item.style.height = "20px";
      vContainer.appendChild(item);
    }

  // 设置父容器高度和宽度，并挂载子结点
  gameEl.style.width = `${gameElHeight}px`;
  gameEl.style.height = `${gameElWidth}px`;
  if (gameEl.hasChildNodes()) gameEl.innerHTML = "";
  gameEl.appendChild(vContainer);
}

// 初始化事件逻辑
function initEventLogic() {
  const gameEl = document.querySelector(".game");
  const isGameItemEl = (target) =>
    Array.from(target.classList).includes("game-item");
  gameEl.addEventListener(
    "click",
    (e) => {
      // 判断游戏是否结束
      if (gameOver()) {
        setMessage("游戏已经结束，点击开始游戏。");
        return;
      }

      const target = e.target;

      if (!isGameItemEl(target)) return;

      const x = Number.parseInt(target.getAttribute("x"));
      const y = Number.parseInt(target.getAttribute("y"));

      // 如果结点已经打开过，直接返回
      if (g[x][y].opened) return;
      // 1. 判断是否是地雷，如果点击了地雷，就直接显示所有地雷。
      if (g[x][y].isMine) {
        // 找到所有是地雷的Dom，把它们颜色修改成红色
        gameStatus = GAME_STATUS.END;

        queueMicrotask(() => {
          const mineEls = document.querySelectorAll(".mine");
          mineEls.forEach((item) => {
            item.style.background = "red";
          });
        });

        setMessage("你完了，你踩到地雷了");

        return;
      }
      // 2. 判断是否不是地雷，不是地雷的如果当前有标记附近的地雷数不等于 0
      else if (g[x][y].mineNumberByAround !== 0) {
        // 修改点击的结点
        g[x][y].opened = true;

        queueMicrotask(() => {
          // 插入文本提示
          target.innerHTML = "" + g[x][y].mineNumberByAround;

          // 修改背景颜色
          target.style.background = "blue";
        });
      } else if (g[x][y].mineNumberByAround === 0) {
        // 如果等于 0 我们需要扩散当前范围，直到没有 空结点可扩散，也就是找连通块
        setVisBlock(x, y);
      }

      // 判断是否排除了所有非雷的元素, 也就是判断是否胜利
      const isWon = checkgStatus();
      if (isWon) {
        gameStatus = GAME_STATUS.WIN;
        setMessage("你赢了！");
      }
    },
    false
  );

  // 监听鼠标右键功能
  // 1. 取消默认事件
  window.oncontextmenu = (e) => e.preventDefault();
  // 2. 监听鼠标右键事件
  window.addEventListener(
    "mousedown",
    (e) => {
      if (e.button !== 2) return;
      const target = e.target;

      if (!isGameItemEl(target)) return;

      const x = Number.parseInt(target.getAttribute("x"));
      const y = Number.parseInt(target.getAttribute("y"));

      // 已经被打开过
      if (g[x][y].opened) return;

      // 查看是否已经被标记过
      if (g[x][y].isFlag) {
        target.style.background = "bisque";
        g[x][y].isFlag = false;
      } else {
        target.style.background = "pink";
        g[x][y].isFlag = true;
      }
    },
    false
  );
}

// 判断游戏是否获胜
function checkgStatus() {
  const [n, m] = [g.length, g[0].length];
  for (let i = 0; i < n; ++i)
    for (let j = 0; j < m; ++j)
      if (!g[i][j].opened && !g[i][j].isMine) return false;
  return true;
}

// 递归设置扩散联通块
function setVisBlock(x, y) {
  g[x][y].opened = true;
  // 得到这个点的DOm，并修改背景色

  queueMicrotask(() => {
    const itemEl = document.querySelector(`.game-item-${x}-${y}`);
    itemEl.style.background = "blue";
    if (g[x][y].mineNumberByAround !== 0)
      itemEl.innerHTML = "" + g[x][y].mineNumberByAround;
  });
  // 如果当前这个点有数字
  if (g[x][y].mineNumberByAround !== 0) {
    return;
  }

  // 如果为0可以继续，扩展这个点周围的点
  const d = [
    [0, 1],
    [0, -1],
    [-1, 0],
    [1, 0],
  ];
  for (let i = 0; i < 4; ++i) {
    const nx = x + d[i][0];
    const ny = y + d[i][1];

    // 判断是否越界
    if (nx < 0 || nx >= g.length || ny < 0 || ny >= g[0].length) continue;

    // 判断是否是炸弹
    if (g[nx][ny].isMine) continue;

    // 判断是否访问过
    if (g[nx][ny].opened) continue;

    setVisBlock(nx, ny);
  }
}

// 游戏结束判断
function gameOver() {
  return gameStatus === GAME_STATUS.END || gameStatus === GAME_STATUS.WIN;
}

// 设置消息
const messageBoxEl = document.querySelector(".message-box");
function setMessage(msg) {
  queueMicrotask(() => {
    messageBoxEl.innerHTML = msg;
  });
}

let isFirst = true;
// 绑定按钮逻辑控制游戏
function initButtonEvent() {
  const beginBtnEl = document.querySelector("#begin");
  beginBtnEl.addEventListener(
    "click",
    () => {
      // 获取行列雷数
      let row = Number.parseInt(inputRowEl.value);
      let col = Number.parseInt(inputColEl.value);
      let mineNumber = Number.parseInt(inputMineNumEl.value);
      if (Number.isNaN(row) || Number.isNaN(col) || Number.isNaN(mineNumber))
        (row = col = 8), (mineNumber = Math.floor(8 * 8 * 0.2));

      initMapArray(row, col, mineNumber);
      initUI();
      setMessage("");
      if (isFirst) {
        initEventLogic();
        isFirst = false;
      }
    },
    false
  );
}

initButtonEvent();
